View Javadoc
1 //============================================================================== 2 // file : Base64.java 3 // project: Java Common Utility 4 // 5 // last change: date: $Date: 2003/09/10 09:22:14 $ 6 // by: $Author: bitiboy $ 7 // revision: $Revision: 1.1 $ 8 //------------------------------------------------------------------------------ 9 // copyright: GNU GPL Software License (see class documentation) 10 //============================================================================== 11 package com.justhis.util; 12 13 14 //////////////////////license & copyright header///////////////////////// 15 // // 16 // Base64 - encode/decode data using the Base64 encoding scheme // 17 // // 18 // Copyright (c) 1998 by Kevin Kelley // 19 // // 20 // This library is free software; you can redistribute it and/or // 21 // modify it under the terms of the GNU Lesser General Public // 22 // License as published by the Free Software Foundation; either // 23 // version 2.1 of the License, or (at your option) any later version. // 24 // // 25 // This library is distributed in the hope that it will be useful, // 26 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 27 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 28 // GNU Lesser General Public License for more details. // 29 // // 30 // You should have received a copy of the GNU Lesser General Public // 31 // License along with this library; if not, write to the Free Software // 32 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 33 // 02111-1307, USA, or contact the author: // 34 // // 35 // Kevin Kelley <kelley@ruralnet.net> - 30718 Rd. 28, La Junta, CO, // 36 // 81050 USA. // 37 // // 38 ////////////////////end license & copyright header/////////////////////// 39 import com.justhis.util.exception.UtilException; 40 41 import java.io.*; 42 43 44 // needed only for main() method. 45 46 /*** 47 * Provides encoding of raw bytes to base64-encoded characters, and decoding of 48 * base64 characters to raw bytes. 49 * 50 * @author Kevin Kelley 51 * @version 1.3 52 */ 53 public class Base64 { 54 //~ Static fields/initializers --------------------------------------------- 55 56 // 57 // code characters for values 0..63 58 // 59 60 /*** TODO */ 61 private static char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" 62 .toCharArray(); 63 64 // 65 // lookup table for converting base64 characters to value in range 0..63 66 // 67 68 /*** TODO */ 69 private static byte[] codes = new byte[256]; 70 71 static { 72 for (int i = 0; i < 256; i++) { 73 codes[i] = -1; 74 } 75 76 for (int i = 'A'; i <= 'Z'; i++) { 77 codes[i] = (byte) (i - 'A'); 78 } 79 80 for (int i = 'a'; i <= 'z'; i++) { 81 codes[i] = (byte) ((26 + i) - 'a'); 82 } 83 84 for (int i = '0'; i <= '9'; i++) { 85 codes[i] = (byte) ((52 + i) - '0'); 86 } 87 88 codes['+'] = 62; 89 codes['/'] = 63; 90 } 91 92 //~ Methods ---------------------------------------------------------------- 93 94 /*** 95 * Decodes a BASE-64 encoded stream to recover the original data. White 96 * space before and after will be trimmed away, but no other manipulation 97 * of the input will be performed. As of version 1.2 this method will 98 * properly handle input containing junk characters (newlines and the 99 * like) rather than throwing an error. It does this by pre-parsing the 100 * input and generating from that a count of VALID input characters. 101 */ 102 public static byte[] decode(char[] data) throws UtilException { 103 // as our input could contain non-BASE64 data (newlines, 104 // whitespace of any sort, whatever) we must first adjust 105 // our count of USABLE data so that... 106 // (a) we don't misallocate the output array, and 107 // (b) think that we miscalculated our data length 108 // just because of extraneous throw-away junk 109 int tempLen = data.length; 110 111 for (int ix = 0; ix < data.length; ix++) { 112 if ((data[ix] > 255) || (codes[data[ix]] < 0)) { 113 --tempLen; // ignore non-valid chars and padding 114 } 115 } 116 117 // calculate required length: 118 // -- 3 bytes for every 4 valid base64 chars 119 // -- plus 2 bytes if there are 3 extra base64 chars, 120 // or plus 1 byte if there are 2 extra. 121 int len = (tempLen / 4) * 3; 122 123 if ((tempLen % 4) == 3) { 124 len += 2; 125 } 126 127 if ((tempLen % 4) == 2) { 128 len += 1; 129 } 130 131 byte[] out = new byte[len]; 132 133 int shift = 0; // # of excess bits stored in accum 134 int accum = 0; // excess bits 135 int index = 0; 136 137 // we now go through the entire array (NOT using the 'tempLen' value) 138 for (int ix = 0; ix < data.length; ix++) { 139 int value = (data[ix] > 255) ? (-1) : codes[data[ix]]; 140 141 if (value >= 0) { // skip over non-code 142 accum <<= 6; // bits shift up by 6 each time thru 143 shift += 6; // loop, with new bits being put in 144 accum |= value; // at the bottom. 145 146 if (shift >= 8) { // whenever there are 8 or more shifted in, 147 shift -= 8; // write them out (from the top, leaving any 148 out[index++] = (byte) ((accum >> shift) & 0xff); // excess at the bottom for next iteration. 149 } 150 } 151 152 // we will also have skipped processing a padding null byte ('=') here; 153 // these are used ONLY for padding to an even length and do not legally 154 // occur as encoded data. for this reason we can ignore the fact that 155 // no index++ operation occurs in that special case: the out[] array is 156 // initialized to all-zero bytes to start with and that works to our 157 // advantage in this combination. 158 } 159 160 // if there is STILL something wrong we just have to throw up now! 161 if (index != out.length) { 162 throw new UtilException("Miscalculated data length (wrote " + index 163 + " instead of " + out.length + ")" 164 ); 165 } 166 167 return out; 168 } 169 170 /*** 171 * returns an array of base64-encoded characters to represent the passed 172 * data array. 173 * 174 * @param data the array of bytes to encode 175 * 176 * @return base64-coded character array. 177 */ 178 public static char[] encode(byte[] data) { 179 char[] out = new char[((data.length + 2) / 3) * 4]; 180 181 // 182 // 3 bytes encode to 4 chars. Output is always an even 183 // multiple of 4 characters. 184 // 185 for (int i = 0, index = 0; i < data.length; i += 3, index += 4) { 186 boolean quad = false; 187 boolean trip = false; 188 189 int val = (0xFF & (int) data[i]); 190 val <<= 8; 191 192 if ((i + 1) < data.length) { 193 val |= (0xFF & (int) data[i + 1]); 194 trip = true; 195 } 196 197 val <<= 8; 198 199 if ((i + 2) < data.length) { 200 val |= (0xFF & (int) data[i + 2]); 201 quad = true; 202 } 203 204 out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)]; 205 val >>= 6; 206 out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)]; 207 val >>= 6; 208 out[index + 1] = alphabet[val & 0x3F]; 209 val >>= 6; 210 out[index + 0] = alphabet[val & 0x3F]; 211 } 212 213 return out; 214 } 215 216 /////////////////////////////////////////////////// 217 // remainder (main method and helper functions) is 218 // for testing purposes only, feel free to clip it. 219 /////////////////////////////////////////////////// 220 public static void main(String[] args) { 221 boolean decode = false; 222 223 if (args.length == 0) { 224 System.out.println("usage: java Base64 [-d[ecode]] filename"); 225 System.exit(0); 226 } 227 228 for (int i = 0; i < args.length; i++) { 229 if ("-decode".equalsIgnoreCase(args[i])) { 230 decode = true; 231 } else if ("-d".equalsIgnoreCase(args[i])) { 232 decode = true; 233 } 234 } 235 236 String filename = args[args.length - 1]; 237 File file = new File(filename); 238 239 if (!file.exists()) { 240 System.out.println("Error: file '" + filename + "' doesn't exist!"); 241 System.exit(0); 242 } 243 244 try { 245 if (decode) { 246 char[] encoded = readChars(file); 247 byte[] decoded = decode(encoded); 248 writeBytes(file, decoded); 249 } else { 250 byte[] decoded = readBytes(file); 251 char[] encoded = encode(decoded); 252 writeChars(file, encoded); 253 } 254 } catch (UtilException e) { 255 e.printStackTrace(); 256 } 257 } 258 259 /*** 260 * TODO 261 * 262 * @param file TODO 263 * 264 * @return TODO 265 * 266 * @throws UtilException TODO 267 */ 268 private static byte[] readBytes(File file) throws UtilException { 269 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 270 271 try { 272 InputStream fis = new FileInputStream(file); 273 InputStream is = new BufferedInputStream(fis); 274 int count = 0; 275 byte[] buf = new byte[16384]; 276 277 while ((count = is.read(buf)) != -1) { 278 if (count > 0) { 279 baos.write(buf, 0, count); 280 } 281 } 282 283 is.close(); 284 } catch (Exception e) { 285 throw new UtilException(e); 286 } 287 288 return baos.toByteArray(); 289 } 290 291 /*** 292 * TODO 293 * 294 * @param file TODO 295 * 296 * @return TODO 297 * 298 * @throws UtilException TODO 299 */ 300 private static char[] readChars(File file) throws UtilException { 301 CharArrayWriter caw = new CharArrayWriter(); 302 303 try { 304 Reader fr = new FileReader(file); 305 Reader in = new BufferedReader(fr); 306 int count = 0; 307 char[] buf = new char[16384]; 308 309 while ((count = in.read(buf)) != -1) { 310 if (count > 0) { 311 caw.write(buf, 0, count); 312 } 313 } 314 315 in.close(); 316 } catch (Exception e) { 317 throw new UtilException(e); 318 } 319 320 return caw.toCharArray(); 321 } 322 323 /*** 324 * TODO 325 * 326 * @param file TODO 327 * @param data TODO 328 * 329 * @throws UtilException TODO 330 */ 331 private static void writeBytes(File file, byte[] data) 332 throws UtilException { 333 try { 334 OutputStream fos = new FileOutputStream(file); 335 OutputStream os = new BufferedOutputStream(fos); 336 os.write(data); 337 os.close(); 338 } catch (Exception e) { 339 throw new UtilException(e); 340 } 341 } 342 343 /*** 344 * TODO 345 * 346 * @param file TODO 347 * @param data TODO 348 * 349 * @throws UtilException TODO 350 */ 351 private static void writeChars(File file, char[] data) 352 throws UtilException { 353 try { 354 Writer fos = new FileWriter(file); 355 Writer os = new BufferedWriter(fos); 356 os.write(data); 357 os.close(); 358 } catch (Exception e) { 359 throw new UtilException(e); 360 } 361 } 362 363 /////////////////////////////////////////////////// 364 // end of test code. 365 /////////////////////////////////////////////////// 366 } 367 368 369 /* 370 * $Log: Base64.java,v $ 371 * Revision 1.1 2003/09/10 09:22:14 bitiboy 372 * *** empty log message *** 373 * 374 * 375 */

This page was automatically generated by Maven